﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Xml;
using UnityEngine.Rendering;

public class Scene
{
    MatterGrid[,] grids;

    public static Scene InitFrom(System.IO.Stream str)
    {
        Scene scene = new Scene();
        System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
        doc.Load(str);
        System.Xml.XmlNode node = doc.SelectSingleNode("Map");
        int Width = int.Parse(node.Attributes["Width"].Value);
        int Height = int.Parse(node.Attributes["Height"].Value);
        scene.grids = new MatterGrid[Width, Height];
        for (int i = 0; i < scene.grids.GetLength(0); i++)
        {
            for (int j = 0; j < scene.grids.GetLength(1); j++)
            {
                scene.grids[i, j] = new MatterGrid(i,j);
            }
        }
        System.Xml.XmlNodeList xnl = doc.SelectNodes("Map/OneGrid");
        foreach(System.Xml.XmlNode xn in xnl)
        {
            int i = int.Parse(xn.Attributes["x"].Value);
            int j = int.Parse(xn.Attributes["y"].Value);
            bool LockTemperature = xn.Attributes["LockTemperature"] == null ? false : bool.Parse(xn.Attributes["LockTemperature"].Value);
            bool CannotDig = xn.Attributes["CannotDig"] == null ? false : bool.Parse(xn.Attributes["CannotDig"].Value);
            bool LockAir = xn.Attributes["LockAir"] == null ? false : bool.Parse(xn.Attributes["LockAir"].Value);
            
            MatterGrid mg = scene.grids[i, j];
            
            float temperature = float.Parse(xn.Attributes["Temperature"].Value);
            mg.SetAllMatterTemperature(temperature);

            mg.CannotDig = CannotDig;
            mg.LockTemperature = LockTemperature;
            mg.LockAir = LockAir;
            if(mg.LockTemperature)
            {
                mg.lockedTemperature = temperature;
            }

            System.Xml.XmlNodeList matterList = xn.SelectNodes("Matter");
            foreach (System.Xml.XmlNode MatterNode in matterList)
            {
                string mattername = MatterNode.Attributes["Name"].Value;
                float matterMass = float.Parse(MatterNode.Attributes["Mass"].Value);
                string matterState = MatterNode.Attributes["MatterState"].Value;
                Matter m = new Matter();
                m.matterType = MatterTypeConfig.AllMatters[mattername];
                m.Mass = matterMass;
                m.currentMatterState = Matter.ConvertStateFromString(matterState);
                m.Temperature = mg.Temperature;
                mg.AddMatter(m);
            }
            if (LockAir)
            {
                mg.LockedAir = new List<Matter>();
                for (int k = 0; k < mg.GasMatter.Count; k++)
                {
                    mg.LockedAir.Add(mg.GasMatter[k].Clone());
                }
            }
        }
        return scene;
    }


    public void SaveToStream(System.IO.Stream str)
    {
        XmlWriterSettings settings = new XmlWriterSettings();
        settings.Indent = true;
        settings.ConformanceLevel = ConformanceLevel.Auto;
        settings.IndentChars = "\t";
        settings.OmitXmlDeclaration = true;
        XmlWriter writer = XmlWriter.Create(str, settings);
        writer.WriteStartElement("Map");
        writer.WriteAttributeString("Width",grids.GetLength(0).ToString());
        writer.WriteAttributeString("Height", grids.GetLength(1).ToString());
        for(int i = 0; i < grids.GetLength(0);i++)
        {
            for(int j = 0; j < grids.GetLength(1);j++)
            {
                writer.WriteStartElement("OneGrid");
                writer.WriteAttributeString("Temperature", grids[i,j].Temperature.ToString());
                writer.WriteAttributeString("x", i.ToString());
                writer.WriteAttributeString("y", j.ToString());
                writer.WriteAttributeString("LockTemperature", grids[i, j].LockTemperature.ToString());
                writer.WriteAttributeString("CannotDig", grids[i, j].CannotDig.ToString());
                writer.WriteAttributeString("LockAir", grids[i, j].LockAir.ToString());

                var matters = grids[i, j].Matters;
                for (int k = 0; k < matters.Count;k++)
                {
                    var m = matters[k];
                    writer.WriteStartElement("Matter");
                    writer.WriteAttributeString("Name",m.matterType.Name);
                    writer.WriteAttributeString("Mass", m.Mass.ToString());
                    writer.WriteAttributeString("MatterState",Matter.ConvertStateToString(m.currentMatterState));

                    writer.WriteEndElement();
                }
                writer.WriteEndElement();
            }
        }
        writer.WriteEndElement();
        writer.Close();
    }



    public void Init(int width, int height)
    {
        grids = new MatterGrid[width, height];
        for (int i = 0; i < grids.GetLength(0); i++)
        {
            for (int j = 0; j < grids.GetLength(1); j++)
            {
                grids[i, j] = new MatterGrid(i,j);
            }
        }
    }

    public int SceneWidth
    {
        get
        {
            return grids.GetLength(0);
        }
    }

    public int SceneHeight
    {
        get
        {
            return grids.GetLength(1);
        }
    }

    public MatterGrid GetGrid(int i, int j)
    {
        return grids[i, j];
    }

    public void TestData()
    {
        for (int i = 0; i < grids.GetLength(0); i++)
        {
            for (int j = 0; j < grids.GetLength(1); j++)
            {
                Matter m = new Matter();
                m.currentMatterState = Matter.MatterState.Solid;
                m.Temperature = UnityEngine.Random.Range(-100.0f, 200.0f);
                m.matterType = MatterTypeConfig.AllMatters["煤"];
                m.Mass = m.matterType.DensityWhenSolid;
                grids[i, j].AddMatter(m);
            }
        }
    }

    public void StartUpdate()
    {
        for (int i = 0; i < grids.GetLength(0); i++)
        {
            for (int j = 0; j < grids.GetLength(1); j++)
            {
                grids[i, j].ResetTime();
            }
        }
        GlobalCoroutine.Instance.StartCoroutine(UpdateCor());
    }


    int maxk = 2;
    int k = 0;
    public IEnumerator UpdateCor()
    {
        while(true)
        {
            for (int i = 0; i < grids.GetLength(0); i++)
            {
                for (int j = 0; j < grids.GetLength(1); j++)
                {
                    if( (i + j * grids.GetLength(0) + k) % maxk == 0)
                    {
                        float gridDtime = grids[i, j].GetAndResetTime();
                        grids[i, j].Update(gridDtime);
                        MergeWithOther(i, j, gridDtime);
                        if(grids[i,j].LockTemperature)
                        {
                            grids[i,j].SetAllMatterTemperature(grids[i,j].lockedTemperature);
                        }
                        if(grids[i,j].LockAir)
                        {
                            for (int m = 0; m < grids[i, j].GasMatter.Count; m++)
                            {
                                grids[i, j].Matters.Remove(grids[i, j].GasMatter[m]);
                            }
                            grids[i, j].GasMatter.Clear();
                            for(int m = 0;m<grids[i,j].LockedAir.Count;m++)
                            {
                                grids[i, j].AddMatter(grids[i, j].LockedAir[m].Clone(grids[i, j].Temperature));
                            }
                        }
                        grids[i, j].BroadCastUpdateEvent();
                    }
                }
            }
            k++;
            if (k == maxk)
            {
                k = 0; 
            }
            yield return 10;
        }

    }

    public void BeginUpdate()
    {
        for (int i = 0; i < grids.GetLength(0); i++)
        {
            for (int j = 0; j < grids.GetLength(1); j++)
            {
                grids[i, j].BeginAirBlock();
            }
        }
    }

    public void EndUpdate()
    {
        for (int i = 0; i < grids.GetLength(0); i++)
        {
            for (int j = 0; j < grids.GetLength(1); j++)
            {
                grids[i, j].EndAirBlock();
            }
        }
    }

    public void Update()
    {
    }


    /// <summary>
    /// 与其他的方块做混合
    /// </summary>
    /// <param name="i"></param>
    /// <param name="j"></param>
    /// <param name="dtime"></param>
    void MergeWithOther(int i, int j,float dtime)
    {
        if (i - 1 >= 0)
        {
            MergeWithOther(grids[i, j], grids[i - 1, j], dtime);
        }
        if (i + 1 < grids.GetLength(0))
        {
            MergeWithOther(grids[i, j], grids[i + 1, j], dtime);
        }
        if (j - 1 >= 0)
        {
            MergeWithOther(grids[i, j], grids[i, j - 1], dtime);
        }
        if (j + 1 < grids.GetLength(1))
        {
            MergeWithOther(grids[i, j], grids[i, j + 1], dtime);
        }
    }

    void MergeWithOther(MatterGrid a ,MatterGrid b,float dtime)
    {
        TransfomHeat(a, b, dtime);
        GasBrownianMovement(a, b, dtime);
        LiquidMove(a, b, dtime);
    }

    static List<Matter> mattersa = new List<Matter>();
    static List<Matter> mattersb = new List<Matter>();

    /// <summary>
    /// 气体之间的布朗运动，会根据 双方的气压 进行修正
    /// </summary>
    /// <param name="a"></param>
    /// <param name="b"></param>
    /// <param name="dtime"></param>
    void GasBrownianMovement(MatterGrid a ,MatterGrid b,float dtime)
    {       
        float surface = MatterGrid.GetAirSurface(a, b);
        if (surface <= 0.0001f)
        {
            return;
        }

        //surface  返回的是  两个格子 相邻的气体面积 .
        float aGasVolume= a.GetGasVolume();
        float bGasVolume = b.GetGasVolume();
        if(aGasVolume == 0.0f || bGasVolume == 0.0f)
        {
            return;
        }
        float minVolume = Mathf.Min(aGasVolume, bGasVolume);
        float minTem = Mathf.Min(a.Temperature,b.Temperature);
        float apercent = minVolume / aGasVolume * GlobalConst.BrownianMovement * dtime * (Mathf.Min(5.0f, (minTem - GlobalConst.AbsoluteZero)) * GlobalConst.BrownianMovementTemperature + 1.0f);
        float bpercent = minVolume / bGasVolume * GlobalConst.BrownianMovement * dtime * (Mathf.Min(5.0f, (minTem - GlobalConst.AbsoluteZero)) * GlobalConst.BrownianMovementTemperature + 1.0f);
        Debug.Assert(!float.IsNaN(apercent));
        Debug.Assert(!float.IsNaN(bpercent));


        float aGasPre = Mathf.Max(a.GetGasPressure(), 1.0f);
        float bGasPre = Mathf.Max(b.GetGasPressure(), 1.0f);

        //气压高的 需要多拿出些气体出来；
        if (aGasPre > bGasPre)
        {
            if(a.Y < b.Y)   //气压高的 且位于下部 会向上传递更多的气体
            {
                apercent += 10.0f * (aGasPre / bGasPre - 1.0f) * dtime;
                bpercent += 2.0f * (1.0f - aGasPre / bGasPre) * dtime;
            }
            else
            {
                apercent += 2.0f * (aGasPre / bGasPre - 1.0f) * dtime;
                bpercent += 2.0f * (1.0f - aGasPre / bGasPre) * dtime;
            }
        }
        if (bGasPre > aGasPre)
        {
            if(b.Y < a.Y)
            {
                bpercent += 10.0f * (bGasPre / aGasPre - 1.0f) * dtime;
                apercent += 2.0f * (1.0f - bGasPre / aGasPre) * dtime;
            }
            else
            {
                bpercent += 2.0f * (bGasPre / aGasPre - 1.0f) * dtime;
                apercent += 2.0f * (1.0f - bGasPre / aGasPre) * dtime;
            }
        }
        Debug.Assert(!float.IsNaN(apercent));
        Debug.Assert(!float.IsNaN(bpercent));


        //根据温度 进行修正  当是上下关系 则温度高的更容易向上传播
        if(b.Y > a.Y && b.Temperature< a.Temperature)
        {
            apercent += 0.1f * (a.Temperature - b.Temperature) * dtime;
            bpercent -= 0.1f * (a.Temperature - b.Temperature) * dtime;
        }
        else if(b.Y < a.Y && b.Temperature > a.Temperature)
        {
            bpercent += 0.1f * (b.Temperature - a.Temperature) * dtime;
            apercent -= 0.1f * (b.Temperature - a.Temperature) * dtime;
        }
        Debug.Assert(!float.IsNaN(apercent));
        Debug.Assert(!float.IsNaN(bpercent));

        //温度超过50度的气体 会有个额外的朝上的量  作弊~~~~

        apercent = Mathf.Clamp(apercent, 0.0f, 0.8f);
        bpercent = Mathf.Clamp(bpercent, 0.0f, 0.8f);


//        if(a.X == 1 && a.Y ==13 && b.X == 1 && b.Y == 12)
//        {
//            Debug.Log("A: " + a.X.ToString() + "  " + a.Y.ToString() + "apercent   " + apercent.ToString());
//            Debug.Log("B: " + b.X.ToString() + "  " + b.Y.ToString() + "bpercent   " + bpercent.ToString());
//        }

//        if (a.X == 1 && a.Y == 12 && b.X == 1 && b.Y == 13)
//        {
//            Debug.Log("A: " + a.X.ToString() + "  " + a.Y.ToString() + "apercent   " + apercent.ToString());
//            Debug.Log("B: " + b.X.ToString() + "  " + b.Y.ToString() + "bpercent   " + bpercent.ToString());
//        }


        float deltagas = apercent - bpercent;
        //记录 移动的方向  用来做 气体的UV 动画
        int index = 0;//0为上 1为右 2 为下  3为左
        int index2 = 0;
        if(a.Y == b.Y)
        {
            if(a.X < b.X)
            {
                index = 1;
                index2 = 3;
            }
            else
            {
                index = 3;
                index2 = 1;
            }
        }
        else if(a.Y < b.Y)
        {
            index = 0;
            index2 = 2;
        }
        else
        {
            index = 2;
            index2 = 0;
        }
        a.gasMovement[index] = deltagas / dtime;
        b.gasMovement2[index2] = -deltagas / dtime;
        //先分别把容器内的内容 拿出来  已防止 温度发生改变 而无法获取到的问题
        mattersa.Clear();
        mattersb.Clear();
        List<Matter> aswapGas = a.RemoveGasMatter(apercent, b.Y - a.Y, ref mattersa);
        List<Matter> bswapGas = b.RemoveGasMatter(bpercent, a.Y - b.Y, ref mattersb);
        for(int i = 0; i < bswapGas.Count;i++)
        {
            bool iskeep = a.AddMatter(bswapGas[i]);
            if(!iskeep)
            {
                Matter.Delete(bswapGas[i]);
            }
        }
        for (int i = 0; i < aswapGas.Count; i++)
        {
            bool iskeep = b.AddMatter(aswapGas[i]);
            if (!iskeep)
            {
                Matter.Delete(aswapGas[i]);
            }
        }
    }

    // 0 为固体 1 为液体 2 为气体
    static float[,] State =
    {
        {10.0f,1.0f,0.02f},
        {1.0f,1.0f,0.02f},
        {0.01f,0.01f,0.01f}
    };

    float TransferState(MatterGrid a,MatterGrid b)
    {
        if(a.Y != b.Y)
        {
            if(a.Y < b.Y)
            {
                MatterGrid temp = a;
                a = b;
                b= temp;
            }
            int x1 = 0;
            int x2 = 0;
            if (a.SolidMatter.Count != 0)
            {
                x1 = 0;
            }
            else if(a.liquidMatterByBoil.Count != 0)
            {
                x1 = 1;
            }
            else
            {
                x1 = 2;
            }
            if(b.GasMatter.Count != 0)
            {
                x2 = 2;
            }
            else if (b.liquidMatterByBoil.Count != 0)
            {
                x2 = 1;
            }
            else
            {
                x2 = 0;
            }
            return State[x1, x2];
        }
        else
        {
            return Mathf.Min(a.GetSolidVolume() * 10.0f + a.GetLiquidVolume() * 1.0f + a.GetGasVolume() * 0.01f , b.GetSolidVolume() * 10.0f + b.GetLiquidVolume() * 1.0f + b.GetGasVolume() * 0.01f);
        }
    }


    void TransfomHeat(MatterGrid a, MatterGrid b,float dtime)
    {
        //传热系数 取 2个物体最小的那一个
        if(a.TotalMass == 0.0f || b.TotalMass == 0.0f)
        {
            return;
        }
        //固体相对于气体来说 传热量 需要降低 气体主要靠对流传递 
        float minht = Mathf.Min(a.AverageHeatTransferCoefficient, b.AverageHeatTransferCoefficient);
        //按照接触面来算 
        minht = minht * TransferState(a, b);
        float dt = (a.Temperature - b.Temperature);
        float denergy = dt * minht * dtime;
        float maybydt = denergy / ((a.AverageSpecificHeat + b.AverageSpecificHeat) * (Mathf.Min(a.TotalMass , b.TotalMass)));
        int count =  (int)(maybydt / dt * 10.0f);
        if( count < 0 )
        {
            count = -count;
        }
        //如果 dtime 或者 minht 过高的话 可能导致 温度传递过量, 这种情况下 应该将dtime 分成若干段,
        //只有作弊性质的导热材质有可能碰到这种情况 因此 暂时不考虑性能问题 直接将 dtime 缩小
        if(count <= 1)
        {
            a.EnergyChange(-denergy);
            b.EnergyChange(denergy);
        }
        else
        {
            for (int i = 0; i < Mathf.Min(10, count); i++)
            {
                dt = (a.Temperature - b.Temperature);
                denergy = dt * minht * (dtime / count);
                a.EnergyChange(-denergy);
                b.EnergyChange(denergy);
            }
        }

    }


    void LiquidMove(MatterGrid a ,MatterGrid b,float dtime)
    {
        //如果 a,b 属于上下级关系 且下级有气体纯在  则上级的液体和下级的气体发生交换 交换相同的体积 
        if(a.Y != b.Y)
        {
            MatterGrid upGrid = a.Y > b.Y ? a : b;
            MatterGrid downGrid = a.Y > b.Y ? b : a;
            float upLiquidVolume = upGrid.GetLiquidVolume();
            float downGasVolume = downGrid.GetGasVolume();
            if(upLiquidVolume != 0.0f && downGasVolume != 0.0f)
            {
                float changevolume = Mathf.Min(upLiquidVolume, downGasVolume);
                mattersa.Clear();
                mattersb.Clear();
                List<Matter> downGas = downGrid.RemoveGasMatter(changevolume / downGasVolume, 0, ref mattersa);
                List<Matter> upLiquid = upGrid.RemoveLiquidMatter(changevolume, ref mattersb);

                for (int i = 0; i < downGas.Count; i++)
                {
                    bool iskeep = upGrid.AddMatter(downGas[i]);
                    if (!iskeep)
                    {
                        Matter.Delete(downGas[i]);
                    }
                }
                for (int i = 0; i < upLiquid.Count; i++)
                {
                    bool iskeep = downGrid.AddMatter(upLiquid[i]);
                    if (!iskeep)
                    {
                        Matter.Delete(upLiquid[i]);
                    }
                }

            }



        }


    }

    public float GetTotalEnergy()
    {
        float ttt = 0.0f;
        for (int i = 0; i < grids.GetLength(0); i++)
        {
            for (int j = 0; j < grids.GetLength(1); j++)
            {
                ttt += grids[i, j].GetTotalEnergy();
            }
        }
        return ttt;
    }

    public float GetTotalMass()
    {
        float ttt = 0.0f;
        for (int i = 0; i < grids.GetLength(0); i++)
        {
            for (int j = 0; j < grids.GetLength(1); j++)
            {
                ttt += grids[i, j].TotalMass;
            }
        }
        return ttt;
    }


    public float GetDataMyType(int x, int y, LayerMesh.DataType type)
    {
        switch(type)
        {
            case LayerMesh.DataType.Temperature:
                return GetDataValue(GetGrid(x, y).Temperature, -30.0f, 0.0f, 0.2f, 40.0f, 0.8f, 80.0f);
            case LayerMesh.DataType.GasPressure:
                return GetDataValue(GetGrid(x, y).GetGasPressure(), 0.0f, 90.0f, 0.2f, 120.0f, 0.8f, 1000.0f);
            case LayerMesh.DataType.O2:
                return GetDataValue(GetGrid(x, y).GetMatterTypeMass("氧气"), 0.0f, 0.1f, 0.3f, 0.3f, 0.8f, 1.0f);
        }

        return 0.0f;
    }


    static float GetDataValue(float v, float startvalue,float p0value, float p0afterChange,float p1value ,float p1AfterChange,float endvalue)
    {
        if(v< startvalue)
        {
            return 0.0f;
        }
        else if(v < p0value)
        {
            return  (v - startvalue) / (p0value - startvalue) * (p0afterChange );
        }
        else if(v < p1value)
        {
            return p0afterChange + (v - p0value) / (p1value - p0value) * (p1AfterChange - p0afterChange);
        }
        else if(v < endvalue)
        {
            return p1AfterChange + (v - p1value) / (endvalue - p1value) * (1.0f - p1AfterChange);
        }
        return 1.0f;
    }

    public void AddShiTou(int x, int y)
    {
        MatterGrid mg = GetGrid(x, y);
        if(mg == null)
        {
            return;
        }
        float sv = mg.GetSolidVolume();
        if(sv > 1.0f)
        {
            return;
        }
        float toaddv = 1.0f - sv;
        Matter m = new Matter();
        m.Temperature = mg.Temperature;
        m.matterType = MatterTypeConfig.AllMatters["石头"];
        m.Mass = m.matterType.DensityWhenSolid * toaddv;
        mg.AddMatter(m);
        List<Matter> newgas = new List<Matter>();
        for(int i = 0 ; i < mg.GasMatter.Count;i++)
        {
            newgas.Add(mg.GasMatter[i]);
        }
        for (int i = 0; i < mg.GasMatter.Count;i++ )
        {
            mg.Matters.Remove(mg.GasMatter[i]);
        }
        mg.GasMatter.Clear();
        int count = 0;
        for (int i = -1; i <= 1; i++)
        {
            for (int j = -1; j <= 1; j++)
            {
                if(i == 0 && j == 0)
                {
                    continue;
                }
                MatterGrid mg1 = MainGame.Instance.Scene.GetGrid(x + i, y + j);
                if (mg1 != null)
                {
                    float sv1 = mg1.GetSolidVolume();
                    if(sv1 <1.0f)
                    {
                        count++;
                    }
                }
            }
        }

        if(count == 0)
        {
            return; //如果附近没有合法的位置去填充气体 则将气体 抛弃掉
        }

        for (int i = -1; i <= 1; i++)
        {
            for (int j = -1; j <= 1; j++)
            {
                if (i == 0 && j == 0)
                {
                    continue;
                }
                MatterGrid mg1 = MainGame.Instance.Scene.GetGrid(x + i, y + j);
                if (mg1 != null)
                {
                    for(int k = 0 ; k < newgas.Count;k++)
                    {
                        var ng = newgas[k].Clone();
                        ng.Mass = newgas[k].Mass / count;
                        mg1.AddMatter(ng);
                    }
                }
            }
        }
    }
    
    public void RemoveSolid(int x,int y)
    {
        MatterGrid mg = GetGrid(x, y);
        if (mg == null)
        {
            return;
        }
        for(int i = 0 ; i < mg.SolidMatter.Count;i++)
        {
            mg.Matters.Remove(mg.SolidMatter[i]);
        }
        mg.SolidMatter.Clear();
        mg.SolidMatterByMassShowPriority.Clear();
    }
}
